#ifndef     __MESSAGE_QUEUE_H
#define     __MESSAGE_QUEUE_H

#include <queue>
#include <mutex>
#include <stdexcept>
#include <condition_variable>

namespace utils {

template <typename T>
class MessageQueue {

public:
    void reset() {
        m_interrupt_flag = false;
    }

    void interrupt() {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_interrupt_flag = true;
        m_cond.notify_one();
    }

    template <typename R = T>
    void put(R&& value) {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_queue.push( std::forward<R>(value) );
        m_cond.notify_one();
    }

    T take() {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cond.wait(lock, [this] () {
            return m_interrupt_flag || (!m_queue.empty());
        });
        if (m_interrupt_flag) {
            throw std::runtime_error("mq interrupt");
        }
        T value = std::move(m_queue.front());
        m_queue.pop();
        return value;
    }

private:
    std::queue<T> m_queue;
    bool m_interrupt_flag = false;
    std::mutex m_mutex;
    std::condition_variable m_cond;
};

}

#endif
